package com.qitmiaojie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.qitmiaojie.controller.utils.IdCreateUtil;
import com.qitmiaojie.controller.utils.Result;
import com.qitmiaojie.mapper.UserMapper;
import com.qitmiaojie.pojo.Material;
import com.qitmiaojie.pojo.Matertask;
import com.qitmiaojie.pojo.User;
import com.qitmiaojie.service.MaterialService;
import com.qitmiaojie.mapper.MaterialMapper;
import com.qitmiaojie.service.MatertaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author Administrator
* @description 针对表【material】的数据库操作Service实现
* @createDate 2023-03-16 15:59:37
*/
@Service
@Slf4j
public class MaterialServiceImpl extends ServiceImpl<MaterialMapper, Material>
    implements MaterialService {


    @Value("${loseTime}")
    private Long loseTime;

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MatertaskService MTService;

    private static final DefaultRedisScript<Long> MATERIAL_SCRIPT;
    static {
        MATERIAL_SCRIPT = new DefaultRedisScript<>();
        MATERIAL_SCRIPT.setLocation(new ClassPathResource("material.lua"));
        MATERIAL_SCRIPT.setResultType(Long.class);
    }


    private static final ExecutorService M_EXECUTOR = Executors.newSingleThreadExecutor();

    @PostConstruct
    private void init(){
        M_EXECUTOR.submit(new MaterialHandler());
    }

    private class MaterialHandler implements Runnable{


        @Override
        public void run() {
            while (true){
                try {
                    List<MapRecord<String, Object, Object>> read = redisTemplate.opsForStream().read(
                            Consumer.from("t1", "c1"),
                            StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                            StreamOffset.create("steam.task", ReadOffset.lastConsumed())

                    );

                    if (read == null || read.isEmpty()){
                        continue;
                    }


                    MapRecord<String, Object, Object> entries = read.get(0);
                    Map<Object, Object> record = entries.getValue();


                    DeductMaterial(
                            Long.parseLong(record.get("id").toString()),
                            Integer.parseInt(record.get("userId").toString()),
                            Integer.parseInt(record.get("materialId").toString())
                    );

                    redisTemplate.opsForStream().acknowledge("steam.task","t1",entries.getId());

                }catch (Exception e){
                    e.printStackTrace();
                }

            }
        }
    }





    /**
     * 领取物资方法实现
     * @param userId 用户Id
     * @param Material 物资ID
     * @return TaskId
     */
    @Override
    public Result AcquireMaterial(Integer userId, Integer Material) {
        Long taskID = new IdCreateUtil().CreateID("task");
        //执行脚本
        int execute =  Objects.requireNonNull(redisTemplate.execute(
                MATERIAL_SCRIPT,
                Collections.emptyList(),
                Material.toString(),
                userId.toString(),
                taskID.toString()
        )).intValue();


        //逻辑判断

        if (execute != 0){
            return Result.error(execute == 1 ? "库存不足":"您已申请并领取过！");
        }else {
            return Result.success("您已领取成功!",2);
        }


    }



    @Override
    public boolean AddMaterial(Material material) {



        log.info("-----{}------",material);
        log.info("-----{}------",material.getMtitle());
        log.info("-----{}------",material.getMdescription());

        boolean save = super.save(material);
        redisTemplate
                .opsForValue()
                .set("material:"+material.getMid(),
                        material.getMstock().toString(),
                        loseTime, TimeUnit.SECONDS
                );
        return save;
//
//        return false;
    }

    @Override
    public Long DeductMaterial(Long TaskId ,Integer userId, Integer MaterialId) {


        super.update(
                new LambdaUpdateWrapper<Material>()
                .eq(Material::getMid,MaterialId)
                        .setSql("mstock = mstock - 1")
        );
        MTService.Create(TaskId,userId,MaterialId);

        return TaskId;
    }

    /**
     * Redis与MySql的数据同步
     * @return
     */
    @Override
    public boolean SyncSqlAndRedis() {
        try {
            //同步物资信息
            List<Material> materialList = super.list(
                    new LambdaQueryWrapper<Material>().
                            select(
                                    Material::getMid,
                                    Material::getMstock
                            ));
            for (Material material : materialList) {
                redisTemplate.opsForValue().set(
                        "material:"+material.getMid(),
                        material.getMstock().toString(),
                        loseTime,
                        TimeUnit.SECONDS
                );
                //同步订单信息
                List<Matertask> taskList = MTService.list(
                        new LambdaQueryWrapper<Matertask>()
                                .eq(Matertask::getMid, material.getMid())
                                .select(Matertask::getUid)
                );

                for (Matertask matertask : taskList) {
                    redisTemplate.opsForSet().add(
                            "task:"+material,
                            matertask.getUid().toString()
                    );
                }

            }
            return true;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public Result findByIdList(Integer currentPage, Integer pageSize) {
        User CONTEXT_USER = userMapper.selectByUsername(SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
        LambdaQueryWrapper<Matertask> eq = new LambdaQueryWrapper<Matertask>().eq(Matertask::getUid, CONTEXT_USER.getId()).orderByDesc(Matertask::getCreatetime);

        IPage<Matertask> page = new Page<>(currentPage,pageSize);
        page = MTService.page(page, eq);
        List<Matertask> Matertasks = page.getRecords();
        List<Material> materialList = Matertasks.stream()
                .map(matertask -> {
                    Material material = getById(matertask.getMid());
                    material.setMcreatetime(matertask.getCreatetime());
                    return material;
                }).collect(Collectors.toList());
        Map<String,Object> map = new HashMap<>();
        map.put("data",materialList);
        map.put("total",page.getTotal());
        return Result.success(map);
    }



}




